package org.jiucheng.instrument;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;

import org.jiucheng.agent.Agent;

public class Inject {
    
    private static final ThreadLocal<String> uuid = new ThreadLocal<String>() {
        protected String initialValue() {
            return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        };
    };
    private static final ThreadLocal<AtomicLong> num = new ThreadLocal<AtomicLong>(){
        protected AtomicLong initialValue() {
            return new AtomicLong();
        };
    };
    private static final ThreadLocal<AtomicLong> step = new ThreadLocal<AtomicLong>() {
        protected AtomicLong initialValue() {
            return new AtomicLong();
        };
    };
    
    public static void before(String className, String methodName, Object[] args) {
        long nl = num.get().incrementAndGet();
        long sl = step.get().incrementAndGet();
        String log;
        if("org.jiucheng.web.filter.DispatcherFilter".equals(className) && "doFilter".equals(methodName)) {
            log = MessageFormat.format("{0} {1,number,#} {2,number,#} B {3,number,#} {4}#{5} {6}", uuid.get(), nl, sl, System.nanoTime(), className, methodName, (args != null && args.length == 3) ? getServletPath(args[0]) : null);
        }else {
            log = MessageFormat.format("{0} {1,number,#} {2,number,#} B {3,number,#} {4}#{5}", uuid.get(), nl, sl, System.nanoTime(), className, methodName);
        }
        appendln(log);
    }
    
    private static Object getServletPath(Object obj) {
        if(obj == null) {
            return null;
        }
        try {
            Method m = obj.getClass().getMethod("getServletPath");
            Object getServletPath = m.invoke(obj);
            Method q = obj.getClass().getMethod("getQueryString");
            Object getQueryString = q.invoke(obj);
            if(getQueryString != null && getQueryString.toString().length() > 0) {
                return getServletPath + "?" + getQueryString;
            }else {
                return getServletPath;
            }
        }catch (IllegalAccessException e) {
            e.printStackTrace();
        }catch (IllegalArgumentException e) {
            e.printStackTrace();
        }catch (InvocationTargetException e) {
            e.printStackTrace();
        }catch (NoSuchMethodException e) {
            e.printStackTrace();
        }catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public static void after(String className, String methodName, Object[] args) {
        long nl = num.get().incrementAndGet();
        long sl = step.get().getAndDecrement();
        String log = MessageFormat.format("{0} {1,number,#} {2,number,#} A {3,number,#} {4}#{5}", uuid.get(), nl, sl, System.nanoTime(), className, methodName);
        if(sl == 1L) {
            uuid.remove();
            num.remove();
            step.remove();
        }
        appendln(log);
    }
    
    public static void exception(String className, String methodName, Throwable e) {
        long nl = num.get().get();
        long sl = step.get().get();
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        pw.println();
        e.printStackTrace(pw);
        pw.close();
        String log = MessageFormat.format("{0} {1,number,#} {2,number,#} E {3,number,#} {4}#{5}{6}", uuid.get(), nl, sl, System.nanoTime(), className, methodName, sw.toString());
        appendln(log);
    }
    
    public static void appendln(String text) {
        Date date = new Date();
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        File file = file(Agent.getProp("webapp.dir") + "log/localhost." + dateFormat.format(date) + ".log");
        appendln(file, text);
    }
    
    public static void appendln(File file, String text) {
        OutputStream os = null;
        BufferedWriter bw = null;
        try {
            os = new FileOutputStream(file, true);
            bw = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
            bw.write(text);
            bw.newLine();
            bw.close();
            os.close();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static File file(String pathName) {
        File file = new File(pathName);
        if(!file.exists() || file.isDirectory()) {
            file = newFile(file);
        }
        return file;
    }
    
    public static File newFile(File file) {
        if(file.exists() && file.isFile()) {
            file.delete();
        }
        String filePath = file.getAbsolutePath().replaceAll("\\\\", "/");
        File tmpDir = new File(filePath.substring(0, filePath.lastIndexOf("/")));
        if(!tmpDir.exists() || !tmpDir.isDirectory()) {
            tmpDir.mkdirs();
        }
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }
}
